import math
import logging
import numpy as np

from util.util_log import setup_logging
from util.util_csv import save_to_csv
from util.util_image import plot_images_3x1, save_images_3x2

from multi_beam_trace.util_phase_pattern import traces_2_phaseBits, phaseBits_2_patterns
from multi_beam_trace.util_create_trace import create_scan_traces_theta, \
    create_scan_traces_phi, create_scan_traces_theta_phi
from multi_beam_trace.util_analysis import get_phase_neighbor_diff, get_antenna_indicators_normalized, compare_arrays


# 配置日志，默认打印到控制台，也可以设置打印到文件
setup_logging()
# setup_logging(log_file="../../files/logs/multi-beam-trace-NN.log")
# 获取日志记录器并记录日志
logger = logging.getLogger("[multi-beam-trace-NN]")


#-------------------------------------------- multi-beam-trace NN: 核心方法 ---------------------------------------------
# 轨迹转换坐标系: (theta, phi) 转 (x, y)
def traces_pitch_2_xy(traces):
    traces_xy = []
    for trace in traces:
        theta = np.deg2rad(trace[0])
        phi = np.deg2rad(trace[1])
        # 计算 x 和 y 坐标
        x = math.sin(theta) * math.sin(phi)
        y = math.sin(theta) * math.cos(phi)
        # 将 (x, y) 添加到结果列表中
        traces_xy.append([x, y])
    return traces_xy


# 计算两点运动法线方向
def calculate_normal_slope(x1, y1, x2, y2):
    # 计算运动方向的斜率
    if x2 - x1 == 0:
        # 垂直情况，运动方向的斜率是无穷大
        v = float('inf')
    else:
        v = (y2 - y1) / (x2 - x1)
    # 计算法线的斜率
    if v == 0 or abs(v) < 0.000001:
        # fixme 水平情况，法线斜率是无穷大 -- 更新 v = 0
        v = 0
        normal_slope = float('inf')
    elif v == float('inf'):
        # 垂直情况，法线斜率是0
        normal_slope = 0
    else:
        # 一般情况，法线斜率是 -1/v
        normal_slope = -1 / v
    return v, normal_slope


# 计算 traces_xy 的运动方向及其法线方向
def get_traces_v_k(traces_xy):
    v_list = []     # 运动方向列表
    k_list = []     # 运动的法线方向列表
    for i in range(len(traces_xy)-1):
        pos2 = traces_xy[i+1]
        pos1 = traces_xy[i]
        x2, y2 = pos2[0], pos2[1]
        x1, y1 = pos1[0], pos1[1]
        v, k = calculate_normal_slope(x1, y1, x2, y2)
        v_list.append(v)
        k_list.append(k)
    return v_list, k_list


# 计算分割线的坐标
def get_divider_y(k, length):
    # 生成 x 列表
    x_list = list(range(length))
    # 计算 y 列表
    y_list = [int(k * xi + length / 2 * (1 - k)) for xi in x_list]
    # 对 y_list 进行检测和修正: 当y_list中的值小于0时等于-1，当y_list中的值大于等于length时等于length-1
    # y_list = [max(0, min(y, length - 1)) for y in y_list]
    y_list = [-1 if y < 0 else (length - 1 if y >= length else y) for y in y_list]
    return x_list, y_list


# 上下合成新码阵
def merge_phases_up_down(phaseBit1, phaseBit2):
    # 确保输入是numpy数组
    if not (isinstance(phaseBit1, np.ndarray) and isinstance(phaseBit2, np.ndarray)):
        raise ValueError("Both inputs must be numpy arrays.")
    # 确保输入是二维数组
    if not (phaseBit1.ndim == 2 and phaseBit2.ndim == 2):
        raise ValueError("Both inputs must be 2D numpy arrays.")
    # 获取行数
    rows1, _ = phaseBit1.shape
    rows2, _ = phaseBit2.shape
    # 计算分割点
    half_rows1 = rows1 // 2
    half_rows2 = rows2 // 2
    # 检查两个数组是否具有相同的列数
    if phaseBit1.shape[1] != phaseBit2.shape[1]:
        raise ValueError("The two input arrays must have the same number of columns.")
    # 创建新的数组，上半部分来自phaseBit1，下半部分来自phaseBit2
    merged_array = np.vstack((phaseBit1[:half_rows1], phaseBit2[half_rows2:]))
    return merged_array


# 左右合成新码阵
def merge_phases_left_right(phaseBit1, phaseBit2):
    # 确保输入是numpy数组
    if not (isinstance(phaseBit1, np.ndarray) and isinstance(phaseBit2, np.ndarray)):
        raise ValueError("Both inputs must be numpy arrays.")
    # 确保输入是二维数组
    if not (phaseBit1.ndim == 2 and phaseBit2.ndim == 2):
        raise ValueError("Both inputs must be 2D numpy arrays.")
    # 获取列数
    _, cols1 = phaseBit1.shape
    _, cols2 = phaseBit2.shape
    # 计算分割点
    half_cols1 = cols1 // 2
    half_cols2 = cols2 // 2
    # 检查两个数组是否具有相同的行数
    if phaseBit1.shape[0] != phaseBit2.shape[0]:
        raise ValueError("The two input arrays must have the same number of rows.")
    # 创建新的数组，左半部分来自phaseBit1，右半部分来自phaseBit2
    merged_array = np.hstack((phaseBit1[:, :half_cols1], phaseBit2[:, half_cols2:]))
    return merged_array


# 倾斜合成新码阵
def merge_phases_oblique(y_list, phaseBit1, phaseBit2):
    # 检查输入数组的形状是否相同
    if phaseBit1.shape != phaseBit2.shape:
        raise ValueError("phaseBit1 and phaseBit2 must have the same shape")
    # 获取输入数组的行数和列数
    rows, cols = phaseBit1.shape
    # 创建一个新的数组，形状为 (rows, cols)
    phaseBit_NN = np.zeros((rows, cols))
    # 遍历每一列
    for i in range(rows):
        y = y_list[i]
        # 将 phaseBit1 的值复制到新数组的上半部分
        phaseBit_NN[:y + 1, i] = phaseBit1[:y + 1, i]
        # 将 phaseBit2 的值复制到新数组的下半部分
        phaseBit_NN[y + 1:, i] = phaseBit2[y + 1:, i]
    return phaseBit_NN


# 由码阵(phaseBits)和法线(k_list)生成码阵变化矩阵
def create_phaseBit_NN(phaseBits, k_list):
    phaseBit_NN_list = []
    # 获取输入数组的行数和列数
    rows, cols = phaseBits[0].shape
    flag_copy = True      # 用于标志轮换拷贝哪边
    for i in range(len(phaseBits)):
        if i == 0:
            phaseBit_NN_list.append(phaseBits[i])
            continue
        k = k_list[i - 1]
        phaseBit_NN_before = phaseBit_NN_list[i - 1]
        # 计算更新一半的码阵
        if k == float('inf'):
            # 左右取半
            if flag_copy:
                phaseBit_NN = merge_phases_left_right(phaseBits[i], phaseBit_NN_before)
            else:
                phaseBit_NN = merge_phases_left_right(phaseBit_NN_before, phaseBits[i])
        elif k == 0:
            # 上下取半
            if flag_copy:
                phaseBit_NN = merge_phases_up_down(phaseBits[i], phaseBit_NN_before)
            else:
                phaseBit_NN = merge_phases_up_down(phaseBit_NN_before, phaseBits[i])
        else:
            # 斜取半
            # 计算分割线坐标
            x_list, y_list = get_divider_y(k, rows)
            if flag_copy:
                phaseBit_NN = merge_phases_oblique(y_list, phaseBits[i], phaseBit_NN_before)
            else:
                phaseBit_NN = merge_phases_oblique(y_list, phaseBit_NN_before, phaseBits[i])
        # 换边
        flag_copy = not flag_copy
        # 记录
        phaseBit_NN_list.append(phaseBit_NN)
    return phaseBit_NN_list


# ============================================== 主方法 =================================================
# NN主方法: 运动方向的法线分割, 交替更新一半
def beam_1_nn(traces, phaseBits):
    # 轨迹角度转直角坐标系
    traces_xy = traces_pitch_2_xy(traces)
    # 计算轨迹角度运动方向的法线 k_list
    v_list, k_list = get_traces_v_k(traces_xy)
    # 由码阵(phaseBits)和法线(k_list)生成码阵变化矩阵
    phaseBit_NN_list = create_phaseBit_NN(phaseBits, k_list)
    return phaseBit_NN_list


# 单波束: 主方法
def main_beam1_nn(bit_num, traces, path_pre, result_file_name):
    # 计算码阵
    phaseBits = traces_2_phaseBits(traces, bit_num)
    # NN
    phaseBits_NN = beam_1_nn(traces, phaseBits)
    # 码阵计算方向图
    patterns = phaseBits_2_patterns(phaseBits, bit_num)
    patterns_NN = phaseBits_2_patterns(phaseBits_NN, bit_num)
    # 计算各种评价指标
    # 1.计算 patterns, pattern_NN 的 PSLL
    pg_list_list, psll_list, psll_coor_list = get_antenna_indicators_normalized(1, patterns)
    pg_list_NN_list, psll_NN_list, psll_coor_NN_list = get_antenna_indicators_normalized(1, patterns_NN)
    # 2.todo 计算 phaseBits_NN 实际指向
    # 3.计算与前一个位置相比改变的码阵数量和
    phaseBit_diff_list, phaseBit_diff_count_list, phaseBit_diff_count_percent_list \
        = get_phase_neighbor_diff(phaseBits)
    phaseBit_diff_list_NN, phaseBit_diff_count_list_NN, phaseBit_diff_count_percent_list_NN \
        = get_phase_neighbor_diff(phaseBits_NN)
    # 保存指标
    for i in range(len(traces)):
        result_csv = {
            "(theta,phi)": traces[i],
            "pg_list": pg_list_list[i],
            "pg_list_NN": pg_list_NN_list[i],
            "psll": psll_list[i],
            "psll_NN": psll_NN_list[i],
            "psll_coor": psll_coor_list[i],
            "psll_coor_NN": psll_coor_NN_list[i],
            "phase_count_diff": phaseBit_diff_count_list[i],
            "phase_count_diff_NN": phaseBit_diff_count_list_NN[i],
            "phase_count_percent_diff": phaseBit_diff_count_percent_list[i],
            "phase_count_percent_diff_NN": phaseBit_diff_count_percent_list_NN[i]
        }
        save_to_csv([result_csv], path_pre + result_file_name)
    # 保存每个指向的图片
    path_img_pre = path_pre + "phase_pattern_diff/"
    for i in range(len(phaseBits)):
        pattern = patterns[i]
        pattern_NN = patterns_NN[i]
        phaseBit = phaseBits[i]
        phaseBit_NN = phaseBits_NN[i]
        if i == 0:
            phaseBit_NN_before = phaseBit_NN
        else:
            phaseBit_NN_before = phaseBits_NN[i-1]
        diff_phaseBit = compare_arrays(phaseBit_NN_before, phaseBit_NN)
        # 画图
        path_img = path_img_pre + str(i) + ".jpg"
        img_text = "(theta,phi)="+str(traces[i]) \
                   + ", count-diff=" + str(phaseBit_diff_count_list[i]) \
                   + "(" + str(phaseBit_diff_count_percent_list[i]) + ")" \
                   + ", count-diff(NN)=" + str(phaseBit_diff_count_list_NN[i]) \
                   + "(" + str(phaseBit_diff_count_percent_list_NN[i]) + ")"
        save_images_3x2(path_img,
                        phaseBit, f"phaseBit(t)", phaseBit_NN_before, f"phaseBit-NN(t-1)", phaseBit_NN, f"phaseBit-NN(t)",
                        pattern, f"pattern", diff_phaseBit, f"diff(phaseBit(t), phaseBit-NN(t))", pattern_NN, f"pattern-NN",
                        img_text)




# 主方法: 多波束动态波束扫描
def main_multi_beam_trace_nn():
    path_pre = f"../files/multi-beam-trace/beam1/NN/"
    # 计算轨迹角度
    traces = create_scan_traces_theta(0, 90, 0)
    # traces = [[0, 90], [30, 90], [45, 90], [60, 90]]
    case_name = "theta(0,1,90)-phi(0,0,0)"
    # 主方法: 多波束动态波束扫描
    main_beam1_nn(1, traces, path_pre + case_name + "/", case_name + ".csv")
    #
    traces = create_scan_traces_theta(0, 90, 90)
    case_name = "theta(0,1,90)-phi(90,0,90)"
    main_beam1_nn(1, traces, path_pre + case_name + "/", case_name + ".csv")
    #
    traces = create_scan_traces_theta(0, 90, 180)
    case_name = "theta(0,1,90)-phi(180,0,180)"
    main_beam1_nn(1, traces, path_pre + case_name + "/", case_name + ".csv")
    #
    traces = create_scan_traces_theta(0, 90, 270)
    case_name = "theta(0,1,90)-phi(270,0,270)"
    main_beam1_nn(1, traces, path_pre + case_name + "/", case_name + ".csv")
    #
    #
    traces = create_scan_traces_phi(10, 0, 360)
    case_name = "theta(10,0,10)-phi(0,1,360)"
    main_beam1_nn(1, traces, path_pre + case_name + "/", case_name + ".csv")
    #
    traces = create_scan_traces_phi(30, 0, 360)
    case_name = "theta(30,0,30)-phi(0,1,360)"
    main_beam1_nn(1, traces, path_pre + case_name + "/", case_name + ".csv")
    #
    traces = create_scan_traces_phi(45, 0, 360)
    case_name = "theta(45,0,45)-phi(0,1,360)"
    main_beam1_nn(1, traces, path_pre + case_name + "/", case_name + ".csv")
    #
    traces = create_scan_traces_phi(60, 0, 360)
    case_name = "theta(60,0,60)-phi(0,1,360)"
    main_beam1_nn(1, traces, path_pre + case_name + "/", case_name + ".csv")
    #
    #
    traces = create_scan_traces_theta_phi(10, 10, 60)
    case_name = "theta(10,1,70)-phi(10,1,70)"
    main_beam1_nn(1, traces, path_pre + case_name + "/", case_name + ".csv")
    #
    traces = create_scan_traces_theta_phi(10, 90, 60)
    case_name = "theta(10,1,70)-phi(90,1,150)"
    main_beam1_nn(1, traces, path_pre + case_name + "/", case_name + ".csv")
    #
    traces = create_scan_traces_theta_phi(10, 180, 60)
    case_name = "theta(10,1,70)-phi(180,1,240)"
    main_beam1_nn(1, traces, path_pre + case_name + "/", case_name + ".csv")


# ====================================================== 测试方法 ===================================================
def test_merge_phases_oblique():
    rows, cols = 64, 64
    array1 = np.full((rows, cols), 1, dtype=int)
    array2 = np.full((rows, cols), 2, dtype=int)
    k = -2
    x_list, y_list = get_divider_y(k, rows)
    array_merge = merge_phases_oblique(y_list, array1, array2)
    print("array1:")
    print(array1)
    print("\narray2:")
    print(array2)
    print("\narray_merge:")
    print(array_merge)
    plot_images_3x1(array1, array2, array_merge)




if __name__ == '__main__':
    logger.info("multi-beam-trace-NN: Rotate and change half of the code array")
    # 主函数
    main_multi_beam_trace_nn()
    # 测试函数
    # test_merge_phases_oblique()